/*******************************************************************
 *
 * Copyright (C), 2021-2022, Letstrong, All rights reserved.
 *
 * Author      : 
 * Create Time : 2020-12-18
 * Version     : v0.1
 * Description : 
 *******************************************************************/
 
/*----------------------------Head file----------------------------*/
#include "user.h"
#include "hal_utc.h"
#include "sys_task.h"
#include "app_skip_alg.h"
#include "app_store_mem.h"
#include "app_global_data.h"
#include "app_store_set_info.h"
#include "ble_app_util.h"
#include "sw_timer_task.h"
#include "user_protocol.h"
#include "user_config.h"

/*----------------------------macro file---------------------------*/
#define USER_PROTO_LOG_EN		0

#if USER_PROTO_LOG_EN
#include "log.h"
#define USER_PROTO_LOG			LOG_DEBUG
#else
#define USER_PROTO_LOG(...)
#endif

#define BLE_RESEND_INV_MS			2300

#define TX_PKT_PAYLOAD_MAX_SIZE		(SKIP_GROUP_MAX_NUM*4 + 20 + 1)

#define app_sync_system_time		hal_utc_seconds_set

/*----------------------------type define--------------------------*/
typedef struct {
	uint8_t frame_seq;
	uint8_t frame_sum;
	uint16_t rest_len;
	uint16_t payload_idx;
}txInfoTace_t;

#pragma pack(1)

typedef struct {
	uint8_t head;
	uint8_t pro_ver;
	uint16_t crc;
	uint8_t cmd;
	uint16_t payload_len;
	uint8_t payload[APP_FIRST_FRAME_DATA_MAX_SIZE];
}first_frame_t;

typedef struct {
	uint8_t frame_seq;
	uint8_t payload[APP_REST_FRAME_DATA_MAX_SIZE];
}rest_frame_t;

typedef struct {
	uint8_t head;
	uint8_t pro_ver;
	uint16_t crc;
	uint8_t cmd;
	uint16_t payload_len;
	uint8_t payload[TX_PKT_PAYLOAD_MAX_SIZE];
}txPkt_t;

typedef struct {
	uint8_t head;
	uint8_t pro_ver;
	uint16_t crc;
	uint8_t cmd;
	uint16_t payload_len;
	uint8_t payload[APP_FIRST_FRAME_DATA_MAX_SIZE];
}rxPkt_t;

#pragma pack()

/*----------------------------var define---------------------------*/
static txInfoTace_t txInfoTrace;
static txPkt_t txPkt;
static uint8_t resend_cnt = 0;
static uint8_t pkt_index = 0;
static uint8_t tmp_pkt_index = 0xFF;

/*-------------------------func declaration------------------------*/
extern void set_cur_clr_secs_cnt(void);

/*-----------------------------------------------------------------*/

bool user_muti_indicate_trans_done(void)
{
	if(txInfoTrace.rest_len <= 0)
		return true;
	else
		return false;
}

static uint16_t checksum(const uint8_t* pData, uint8_t len)
{
	uint16_t chksum = 0;
	for(uint16_t i=0;i<len;i++) {
		chksum += pData[i];
	}
	return chksum;
}

static void print_hex(const uint8_t *data, uint16_t len)
{
	for(uint16_t i=0;i<len;i++) {
		USER_PROTO_LOG("%x, ",data[i]);
	}
	USER_PROTO_LOG("\n");
}


void app_skip_mem_delay_send_timer_start(void)
{
	ble_delay_send_timer_start(BLE_RESEND_INV_MS);
	USER_PROTO_LOG("app_skip_mem_delay_send_timer_start.\n");
}


void app_skip_mem_delay_send_timeout_handle(void)
{
    app_ble_upload_skip_result();
}


void app_skip_mem_resend_timer_start(void)
{
	resend_cnt = 0;
	ble_resend_timer_start(BLE_RESEND_INV_MS);
	USER_PROTO_LOG("app_skip_mem_resend_timer_start.\n");
}


void app_skip_mem_resend_timeout_handle(void)
{
	resend_cnt++;
	if(resend_cnt < 3) {
		if(true == user_upload_unsent_skip_data(false)) {
			USER_PROTO_LOG("resend_cnt: %d.\n", resend_cnt);
			ble_resend_timer_start(BLE_RESEND_INV_MS);
		}
	}
}


static bool rope_first_frame_upload(txPkt_t* txPkt)
{
	struct user_ind_upd_req indic;
	memset(&indic, 0, sizeof(indic));
	
	if(txPkt->payload_len > APP_FIRST_FRAME_DATA_MAX_SIZE) {
		indic.len = APP_FIRST_FRAME_DATA_MAX_SIZE + APP_PACKET_HEAD_SIZE;
	}
	else {
		indic.len = txPkt->payload_len + APP_PACKET_HEAD_SIZE;
	}
	
	memcpy(indic.value, (uint8_t*)txPkt, indic.len);
	if(true == user_indicate_data(&indic)) {
		USER_PROTO_LOG("tx: ");
		print_hex(indic.value, indic.len);
	}

	return true;
}

static bool rope_rest_frame_upload(txInfoTace_t* trace, uint8_t len)
{
	if(len > APP_REST_FRAME_DATA_MAX_SIZE) {
		return false;
	}

	struct user_ind_upd_req indic;
	rest_frame_t* tx = (rest_frame_t*)indic.value;
	
	memset(&indic, 0, sizeof(indic));
	
	indic.len = len + 1;
	tx->frame_seq = trace->frame_seq;
	memcpy(tx->payload, &txPkt.payload[trace->payload_idx], len);
	
	if(true == user_indicate_data(&indic)) {
		USER_PROTO_LOG("tx rest: ");
		print_hex(indic.value, indic.len);
	}
	return true;
}

static bool rope_trans_upload(uint8_t cmd, const uint8_t* pData, uint16_t len)
{
	if(len > TX_PKT_PAYLOAD_MAX_SIZE)
		return false;
	
	txPkt.head = PRO_HEAD_B1;
	txPkt.pro_ver = PRO_VER_B2;
	txPkt.cmd = cmd;
	txPkt.payload_len = len;
	memcpy(&txPkt.payload, pData, len);
	txPkt.crc = checksum(&txPkt.cmd, txPkt.payload_len + 3);
	
	txInfoTrace.payload_idx = 0;
	txInfoTrace.rest_len = txPkt.payload_len;
	txInfoTrace.frame_sum = 1;
	if(txInfoTrace.rest_len > APP_FIRST_FRAME_DATA_MAX_SIZE) {
		txInfoTrace.frame_sum =  (txInfoTrace.rest_len - APP_FIRST_FRAME_DATA_MAX_SIZE)/APP_REST_FRAME_DATA_MAX_SIZE + 1;
	}
	
	rope_first_frame_upload(&txPkt);
	if(txInfoTrace.rest_len > APP_FIRST_FRAME_DATA_MAX_SIZE) {
		txInfoTrace.rest_len -= APP_FIRST_FRAME_DATA_MAX_SIZE;
	}
	else {
		txInfoTrace.rest_len = 0;
	}

	return true;
}

bool user_upload_unsent_skip_data(bool increase)
{
	uint32_t unsent_size = 0;
	
	if(BLE_CONN_TYPE_PHONE_APP != g_app_data.sys.ble_conn_type)
		return false;
	
	unsent_size = app_mem_unsent_data_size_get();

	USER_PROTO_LOG("unsent_size: %d.\n", unsent_size);
	if(unsent_size <= 0) {
		return false;
	}

	memset(&txPkt, 0, sizeof(txPkt));
	memset(&txInfoTrace, 0, sizeof(txInfoTrace));
	
	app_mem_unsent_skip_data_read(txPkt.payload, sizeof(skip_packet_t));
	skip_packet_t* skip_pack = (skip_packet_t*)txPkt.payload;

#if (UART_PRINTF_EN)	//Debug
	USER_PROTO_LOG("skip_cnt: %d.\n", skip_pack->skip_cnt_sum);
	USER_PROTO_LOG("skip_secs: %d.\n", skip_pack->skip_sec_sum);
	for(uint8_t i=0;i<=skip_pack->trip_num;i++) {
		USER_PROTO_LOG("c_sec: %d.\n", skip_pack->group[i].skip_sec);
		USER_PROTO_LOG("c_cnt: %d.\n", skip_pack->group[i].skip_cnt);
	}
#endif
	
	txPkt.head = PRO_HEAD_B1;
	txPkt.pro_ver = PRO_VER_B2;
	if(unsent_size <= sizeof(save_skip_packet_t)) {
		txPkt.cmd = SKIP_CMD_REALTIME_RET;
	}
	else {
		txPkt.cmd = SKIP_CMD_HISTORY_RET;
	}
	USER_PROTO_LOG("len: %d, %d\n", sizeof(skip_packet_t) - SKIP_GROUP_MAX_NUM*sizeof(skip_group_t), skip_pack->trip_num);
	txPkt.payload_len = (sizeof(skip_packet_t) - SKIP_GROUP_MAX_NUM*sizeof(skip_group_t) + (skip_pack->trip_num+1)*sizeof(skip_group_t) + 1);	
	memcpy(&txPkt.payload[txPkt.payload_len-3], (uint8_t*)&skip_pack->skip_valid_sec, sizeof(uint16_t)); //Patch for master calorie algorithm.
    if(increase) {
        pkt_index++;
        USER_PROTO_LOG("pkt_index increase: %d.\n", pkt_index);
        txPkt.payload[txPkt.payload_len - 1] = pkt_index;  //Patch for increase index.
    }
    else {
        txPkt.payload[txPkt.payload_len - 1] = pkt_index;  //Patch for increase index.
    }
	txPkt.crc = checksum(&txPkt.cmd, txPkt.payload_len + 3);
	
	txInfoTrace.payload_idx = 0;
	txInfoTrace.rest_len = txPkt.payload_len;
	txInfoTrace.frame_sum = 1;
	if(txInfoTrace.rest_len > APP_FIRST_FRAME_DATA_MAX_SIZE) {
		txInfoTrace.frame_sum += (txInfoTrace.rest_len - APP_FIRST_FRAME_DATA_MAX_SIZE)/APP_REST_FRAME_DATA_MAX_SIZE + 1;
	}
	
	rope_first_frame_upload(&txPkt);
	if(txInfoTrace.rest_len > APP_FIRST_FRAME_DATA_MAX_SIZE) {
		txInfoTrace.payload_idx += APP_FIRST_FRAME_DATA_MAX_SIZE;
		txInfoTrace.rest_len -= APP_FIRST_FRAME_DATA_MAX_SIZE;
	}
	else {
		txInfoTrace.rest_len = 0;
	}

	return true;
}


void user_upload_info_to_app_process(uint8_t cmd)
{
	uint8_t tx_data[APP_FIRST_FRAME_DATA_MAX_SIZE];
	
	if(BLE_CONN_TYPE_PHONE_APP != g_app_data.sys.ble_conn_type)
		return;
	
	if(SKIP_CMD_REST_DATA == cmd && txInfoTrace.rest_len <= 0)
		return;

	if(SKIP_CMD_DISP_DATA == cmd && false == user_muti_indicate_trans_done())	//Prevent effecting skip memory data send.
		return;
	
	if(SKIP_CMD_REST_DATA != cmd) {
		txInfoTrace.rest_len = 0;
	}

	memset(tx_data, 0, sizeof(tx_data));

	switch(cmd)
	{
		case SKIP_CMD_REST_DATA: {
			//Send rest frames.
			txInfoTrace.frame_seq++;
			if(txInfoTrace.frame_seq < txInfoTrace.frame_sum) {
				USER_PROTO_LOG("Rest: %d, %d, %d, %d.\n", txInfoTrace.frame_seq, txInfoTrace.frame_sum, txInfoTrace.rest_len, txInfoTrace.payload_idx);
				if(txInfoTrace.rest_len < APP_REST_FRAME_DATA_MAX_SIZE) {
					rope_rest_frame_upload(&txInfoTrace, txInfoTrace.rest_len);
					txInfoTrace.rest_len = 0;
				}
				else {
					rope_rest_frame_upload(&txInfoTrace, APP_REST_FRAME_DATA_MAX_SIZE);
					txInfoTrace.payload_idx += APP_REST_FRAME_DATA_MAX_SIZE;
					txInfoTrace.rest_len -= APP_REST_FRAME_DATA_MAX_SIZE;
				}
			}
		} break;

		case SKIP_CMD_DISP_DATA: {
			user_skip_disp_data_t* disp_data = (user_skip_disp_data_t*)tx_data;
			disp_data->mode = g_app_data.tmp.skip.mode;
			if(SKIP_MODE_TIME == disp_data->mode) {
				disp_data->setting = g_app_data.user.cnt_down_secs;
			}
			else if(SKIP_MODE_COUNT == disp_data->mode) {
				disp_data->setting = g_app_data.user.cnt_down_jumps;
			}
			disp_data->skip_sec_sum = g_app_data.tmp.skip.skip_sec_sum;
			disp_data->skip_cnt_sum = g_app_data.tmp.skip.skip_cnt_sum;
			disp_data->trip_num = g_app_data.tmp.skip.trip_num;
			disp_data->batt_per = g_app_data.batt.disp_percent;
			disp_data->skip_valid_sec = g_app_data.tmp.skip.skip_valid_sec;

			rope_trans_upload(SKIP_CMD_DISP_DATA, tx_data, sizeof(user_skip_disp_data_t));
		} break;

		case SKIP_CMD_FAC_TEST: {
			USER_PROTO_LOG("UP_FAC_TEST_RSP\n");
			rope_trans_upload(SKIP_CMD_FAC_TEST, tx_data, 1);
		} break;
		
#if(DEV_MAC_BURN_ENABLE)
		case SKIP_CMD_GO_BURN_MODE: {
			rope_trans_upload(SKIP_CMD_GO_BURN_MODE, tx_data, 1);
		} break;
		
		case SKIP_CMD_BURN_MAC: {
			USER_PROTO_LOG("UP_BURN_MAC_RSP\n");
			if(DEV_MAC_LEN == app_set_info_burn_mac_get(&tx_data[1])) {
				tx_data[0] = DEV_MAC_VALID;
			}
			else {
				tx_data[0] = DEV_MAC_INVALID;
			}
			rope_trans_upload(SKIP_CMD_BURN_MAC, tx_data, DEV_MAC_LEN+1);
		} break;
#endif
 
		case SKIP_CMD_DEV_RESET: {
			rope_trans_upload(SKIP_CMD_DEV_RESET, tx_data, 1);
		} break;
		
		case SKIP_CMD_DEV_REVERT: {
			rope_trans_upload(SKIP_CMD_DEV_REVERT, tx_data, 1);
		} break;
		
		case SKIP_CMD_SET_ADV_NAME: {
			uint8_t name_len = app_set_info_ble_adv_name_get(&tx_data[1]);
			tx_data[0] = name_len;
			rope_trans_upload(SKIP_CMD_SET_ADV_NAME, tx_data, name_len+1);
		} break;

		default:
			break;
	}
}


void ble_rope_client_write_rsp(const uint8_t* pData, uint16_t len)
{
	USER_PROTO_LOG("RX Data:");
	print_hex(pData, len);

	if(len > sizeof(rxPkt_t))
		return;

	rxPkt_t* rx = (rxPkt_t*)pData;
	
	if(PRO_HEAD_B1 != rx->head || PRO_VER_B2 != rx->pro_ver) {
		USER_PROTO_LOG("Err pro id: %x, %x.\n", rx->head, rx->pro_ver);
		return;
	}

	if(rx->crc != checksum(&rx->cmd, rx->payload_len + 3)) {
		USER_PROTO_LOG("Err chksum: %x, %x.\n", rx->crc, checksum(&rx->cmd, rx->payload_len + 3));
		return;
	}

	switch(rx->cmd)
	{
		case SKIP_CMD_SYNC_TIME: {
			user_sync_time_t* time = (user_sync_time_t*)rx->payload;
			USER_PROTO_LOG("DL_CMD_SYNC_TIME: %u, %d.\n", time->utc, time->timezone_mins);
			app_set_info_local_timezone_save(time->timezone_mins);
			app_sync_system_time(time->utc);
            set_cur_clr_secs_cnt();
		} break;
		
		case SKIP_CMD_START_SKIP: {
			user_start_skip_t* start_skip = (user_start_skip_t*)rx->payload;
			USER_PROTO_LOG("DL_CMD_START_SKIP: %u, %d.\n", start_skip->utc, start_skip->mode);
			app_sync_system_time(start_skip->utc);
            set_cur_clr_secs_cnt();
			
			if(start_skip->mode < SKIP_MODE_MAX) {
				//Save not done skip data firstly.
				if(g_app_data.tmp.skip.skip_cnt_sum > 0 && false == g_app_data.sys.state.skip_done) {
					app_skip_result_calc_and_save();
				}
				
				if(SKIP_MODE_TIME == start_skip->mode) {
					app_set_info_cnt_down_secs_save(start_skip->setting);
					
				}
				else if(SKIP_MODE_COUNT == start_skip->mode) {
					app_set_info_cnt_jump_num_save(start_skip->setting);
				}
				
				g_app_data.tmp.tmp_skip_mode = start_skip->mode;
				
				if(start_skip->cnt_down_start > START_SKIP_SECS_MAX_VAL)
					start_skip->cnt_down_start = START_SKIP_SECS_MAX_VAL;
				
				if(start_skip->cnt_down_start > 0) {
					g_app_data.tmp.cnt_down_start = start_skip->cnt_down_start;
					sys_task_evt_set(SYS_EVT_SKIP_CNT_DOWN_START);
				}
				else {
					sys_task_evt_set(SYS_EVT_APP_SKIP_TIME_MODE + start_skip->mode);
				}
			}
		} break;
		
		case SKIP_CMD_STOP_SKIP: {
            if(SKIP_MODE_CUMULATIVE_NUM != g_app_data.tmp.skip.mode)
            {
                if(false == g_app_data.sys.state.skip_done && g_app_data.tmp.skip.skip_cnt_sum > 0) {
                    //Ignore the stop cmd from App if skip have done.
                    g_app_data.sys.state.skip_done = true;
                    sys_task_evt_set(SYS_EVT_SKIP_DONE);
                }
            }
		} break;
		
		case SKIP_CMD_REALTIME_RET:
		case SKIP_CMD_HISTORY_RET: {
            if(1 == rx->payload_len) {  //Old version
                USER_PROTO_LOG("DL_CMD_RSP: %x, %x.\n", rx->payload[0]);
                if(USER_PROTO_SUCCESS == rx->payload[0]) {	//Get send skip data rsp.
                    ble_resend_timer_stop();
                    app_mem_sent_data_abandon();
                    /* Set next skip data sending into sys evt. If send data here, may cause send indication fail.
                        It may caused by sending data in the same conn interval. */
                    sys_task_evt_set(SYS_EVT_BLE_UPLOAD_MEM);
                }
            }
            else if(2 == rx->payload_len) { //New version
                if(USER_PROTO_SUCCESS == rx->payload[0] && pkt_index == rx->payload[1]) {	//Get send skip data rsp.
                    if(tmp_pkt_index != pkt_index) {
                        ble_resend_timer_stop();
                        app_mem_sent_data_abandon();
                        tmp_pkt_index = pkt_index;
                        if(app_mem_unsent_data_size_get() > 0) {
                            /* Set next skip data sending into sys evt. If send data here, may cause send indication fail.
                                It may caused by sending data in the same conn interval. */
                            sys_task_evt_set(SYS_EVT_BLE_UPLOAD_MEM);
                        }
                    }
                    else {
                        USER_PROTO_LOG("Repeat rsp, we filter it.\n");
                    }
                }
            }
		} break;
		
		case SKIP_CMD_FAC_TEST: {
			USER_PROTO_LOG("DL_CMD_FAC_TEST.\n");
			if(0 == rx->payload[0]) {
				sys_task_evt_set(SYS_EVT_BOARD_TEST);
			}
			else if(1 == rx->payload[0]) {
				sys_task_evt_set(SYS_EVT_DEV_TEST);
			}
		} break;
		
#if(DEV_MAC_BURN_ENABLE)
		case SKIP_CMD_GO_BURN_MODE: {
			USER_PROTO_LOG("DL_CMD_GO_BURN_MODE.\n");
			sys_task_evt_set(SYS_EVT_ENTER_BURN_MAC_MODE);
		} break;
		
		case SKIP_CMD_BURN_MAC: {
			USER_PROTO_LOG("DL_CMD_BURN_MAC: %d.\n", rx->payload_len);
			app_set_info_burn_mac_save(&rx->payload[0], rx->payload_len);
			sys_task_evt_set(SYS_EVT_BURN_MAC);
		} break;
#endif

		case SKIP_CMD_DEV_RESET: {
			sys_task_evt_set(SYS_EVT_BLE_DEV_RESET);
		} break;
		
		case SKIP_CMD_DEV_REVERT: {
			sys_task_evt_set(SYS_EVT_BLE_DEV_REVERT);
		} break;
		
		case SKIP_CMD_SET_ADV_NAME: {
			uint8_t name_len = rx->payload[0];
			if(true == app_set_info_ble_adv_name_save(&rx->payload[1], name_len)) {
				sys_task_evt_set(SYS_EVT_BLE_SET_ADV_NAME);
			}
		} break;

		default:
			break;
		//unknown command
	}
}
